package com.business.activiti.service;

import com.business.activiti.model.auto.AppealInfo;
import com.business.activiti.model.auto.TaskNode;
import com.business.activiti.model.auto.TaskNodeExample;
import com.business.appealtype.model.auto.AppealType;
import com.business.appealtype.service.AppealTypeService;
import com.business.form.model.auto.*;
import com.business.form.service.FormFieldInfoService;
import com.business.form.service.FormInfoService;
import com.business.form.service.FormInstanceFieldValueService;
import com.business.form.service.FormInstanceService;
import com.business.system.model.auto.TsysRole;
import com.business.system.model.auto.TsysUser;
import com.business.system.service.SysRoleService;
import com.business.system.service.SysUserService;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.StartEvent;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.UnsupportedEncodingException;
import java.util.*;

@Service
public class ActivitiService {
    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private IdentityService identityService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private SysUserService userService;


    @Autowired
    private TaskService taskService;

    @Autowired
    ProcessEngine processEngine;

    @Autowired
    private AppealInfoService appealInfoService;

    @Autowired
    private FormFieldInfoService formFieldInfoService;

    @Autowired
    private FormInfoService formInfoService;

    @Autowired
    private FormInstanceService formInstanceService;

    @Autowired
    private SysRoleService sysRoleService;

    @Autowired
    private FormInstanceFieldValueService formInstanceFieldValueService;

    @Autowired
    private TaskNodeService taskNodeService;

    @Autowired
    private AppealTypeService appealTypeService;

    public Model addNewModel(){
        //初始化一个空模型
        Model model = repositoryService.newModel();

        //设置一些默认信息
        String name = "new-process";
        String description = "";
        int revision = 1;
        String key = "process";

        ObjectNode modelNode = objectMapper.createObjectNode();
        modelNode.put(ModelDataJsonConstants.MODEL_NAME, name);
        modelNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, description);
        modelNode.put(ModelDataJsonConstants.MODEL_REVISION, revision);

        model.setName(name);
        model.setKey(key);
        model.setMetaInfo(modelNode.toString());

        repositoryService.saveModel(model);
        String id = model.getId();

        //完善ModelEditorSource
        ObjectNode editorNode = objectMapper.createObjectNode();
        editorNode.put("id", "canvas");
        editorNode.put("resourceId", "canvas");
        ObjectNode stencilSetNode = objectMapper.createObjectNode();
        stencilSetNode.put("namespace",
                "http://b3mn.org/stencilset/bpmn2.0#");
        editorNode.put("stencilset", stencilSetNode);
        try {
            repositoryService.addModelEditorSource(id,editorNode.toString().getBytes("utf-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return model;
    }

    /**
     * 流程模型发布为流程
     * @param modelId
     */
    public Deployment modelDeploy(String modelId){
        try {
            Model modelData = repositoryService.getModel(modelId);
            ObjectNode modelNode = (ObjectNode) new ObjectMapper()
                    .readTree(repositoryService.getModelEditorSource(modelData.getId()));
            byte[] bpmnBytes = null;

            BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
            bpmnBytes = new BpmnXMLConverter().convertToXML(model);

            String processName = modelData.getName() + ".bpmn20.xml";


            //这里把model的id存放到Deployment的key中，方便找到model
            Deployment deployment = repositoryService.createDeployment()
                    .name(modelData.getName()).key(modelData.getId()).addString(processName, new String(bpmnBytes,"UTF-8"))
                    .deploy();

            return deployment;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /*
    根据模型key(act_re_model里的key) 获取最近版本的流程定义
     */
    public ProcessDefinition findLastVersionProcessDefinitionByModelKey(String modelKey){
        Model model=repositoryService.createModelQuery().modelKey(modelKey).singleResult();
        if(model!=null){
            List<Deployment> deployments=repositoryService.createDeploymentQuery().orderByDeploymenTime().desc().deploymentKey(model.getId()).list();
            if(deployments!=null&&deployments.size()>0){
                ProcessDefinition pd=repositoryService.createProcessDefinitionQuery().deploymentId(deployments.get(0).getId()).singleResult();
                return pd;
            }
        }
        return null;
    }

    /*
        根据模型key(act_re_model里的key) 获取最近版本的流程定义的 起始按钮的表单key
    */
    public String getProcessDefinitionStartElementFormKey(String modelKey){
        //repositoryService.createProcessDefinitionQuery().latestVersion().
        ProcessDefinition pd=findLastVersionProcessDefinitionByModelKey(modelKey);
        if(pd!=null){
            BpmnModel model = repositoryService.getBpmnModel(pd.getId());
            if(model != null) {
                Collection<FlowElement> flowElements = model.getMainProcess().getFlowElements();
                for(FlowElement e : flowElements) {
                    if("StartEvent".equals(e.getClass().getSimpleName())){
                        StartEvent se=(StartEvent) e;
                        return se.getFormKey();
                    }
                }
            }
        }
        return null;
    }

    /**
     * 启动流程 增加一个流程实例 增加一个诉求信息
     * @param map 流程启动所需要的表单字段值
     * @param userId 用户id
     * @param appealSource 注册来源
     */
    public void startProcessAndAppeal(HashMap<String,String> map,String appealTypeId,String userId,String appealSource){
        AppealType appealType =appealTypeService.selectByPrimaryKey(appealTypeId);
        if(appealType!=null){
            // modelKey 该modelKey对应 act_re_model里面的key值 也对应诉求类型里面关联的key
            String modelKey=appealType.getProcessKey();

            ProcessDefinition pd=findLastVersionProcessDefinitionByModelKey(modelKey);
            String startElementFormKey=getProcessDefinitionStartElementFormKey(modelKey);
            if(pd!=null){
                //启动流程 生成流程实例
                Map<String,Object> vmap=new HashMap<String,Object>();
                //判断表单元素是否有网关字段
                if(StringUtils.isNotBlank(map.get("gf_gateway_value"))){
                    vmap.put("gf_gateway_value",map.get("gf_gateway_value"));
                }
                //初始化 诉求实例
                AppealInfo ai=new AppealInfo();
                ai.setAppealTypeId(null);
                ai.setCreateTime(new Date());
                ai.setUserId(userId);
                ai.setIsDel(0);
                ai.setProcessDefinitionId(pd.getId());
                ai.setAppealTypeId(appealTypeId);
                appealInfoService.insertSelective(ai);

                //activiti启动流程 ProcessInstance为流程实例
                ProcessInstance pi=runtimeService.startProcessInstanceById(pd.getId(),ai.getId()+"",vmap);

                //生成流程开始节点表单实例
                FormInstance fi=initFormInstanceAndFormValue(startElementFormKey,map,ai.getId(),null,"start");
                ai.setProcessInstanceId(pi.getId());
                if(fi!=null){
                    ai.setStartEventFormId(fi.getId());
                }
                appealInfoService.updateByPrimaryKeySelective(ai);
            }
        }
    }

    /**
     *
     * @param formKey 表单定义id
     * @param map 表单字段的值
     * @param appealInfoId 业务id
     * @param taskId 任务id
     * @param eventType 节点标识 现在暂时有两种 1.task 2.start
     */
    public FormInstance initFormInstanceAndFormValue(String formKey,Map<String,String> map,String appealInfoId,String taskId,String eventType){
        FormInfo formInfo=formInfoService.selectByPrimaryKey(formKey);
        if(formInfo!=null){
            //获取表单定义下的字段
            FormFieldInfoExample testExample=new FormFieldInfoExample();
            testExample.createCriteria().andFormIdEqualTo(formKey);
            List<FormFieldInfo> fields=formFieldInfoService.selectByExample(testExample);
            if(fields!=null&&fields.size()>0){
                FormInstance fi=new FormInstance();
                fi.setAppealInfoId(appealInfoId);
                fi.setCreateTime(new Date());
                fi.setFormInfoId(formInfo.getId()+"");
                formInstanceService.insertSelective(fi);

                for(FormFieldInfo field:fields){
                    //存入表单实例对应的字段值
                    FormInstanceFieldValue value=new FormInstanceFieldValue();
                    value.setFieldLabel(field.getFieldLabel());
                    value.setFieldName(field.getFieldName());
                    value.setFieldType(field.getFieldType());
                    value.setFormInstanceId(fi.getId());
                    value.setValue(map.get(field.getFieldName()));
                    formInstanceFieldValueService.insertSelective(value);
                }
                return fi;
            }
        }
        return null;
    }

    /**
     * 根据用户获取待处理的任务
     * @param user
     * @return
     */
    public List<Task> getTaskListByUser(TsysUser user){
        if(user==null){
            return null;
        }
        List<TsysRole> roleList=sysRoleService.queryUserRole(user.getId());
        List<String> groupIds=new ArrayList<String>();
        for(TsysRole role:roleList){
            groupIds.add(role.getId());
        }
        List<Task> userTasks=taskService.createTaskQuery().taskCandidateOrAssigned(user.getId()).list();
        List<Task> roleTasks=taskService.createTaskQuery().taskCandidateGroupIn(groupIds).list();
        List<String> taskIds=new ArrayList<String>();
        for(Task task:userTasks){
            taskIds.add(task.getId());
        }
        for(Task task:roleTasks){
            if(!taskIds.contains(task.getId())){
                userTasks.add(task);
            }
        }
        return userTasks;
    }

    /**
     * 根据用户获取已处理的任务
     * @param user
     * @return
     */
    public List<HistoricTaskInstance> getCompletedTaskListByUser(TsysUser user){
        if(user==null){
            return null;
        }
        List<HistoricTaskInstance> list=historyService.createHistoricTaskInstanceQuery().taskOwner(user.getId()).list();
        return list;
    }

    //根据任务id完成任务

    /**
     *
     * @param taskId 任务id
     * @param map 任务表单
     * @param user 任务完成人
     */
    public void completeTaskById(String taskId,HashMap<String,String> map,TsysUser user){
        //此处不再逻辑判断当前用户可否完成该任务 默认当前登录用户可以完成该任务
        //
        Task task=taskService.createTaskQuery().taskId(taskId).singleResult();

        //task.setass
        if(task!=null){
            taskService.setOwner(taskId,user.getId());
            //查询出流程实例 流程实例中有业务key
            ProcessInstance pi=runtimeService.createProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
            //生成本地系统任务节点

            Map<String,Object> vmap=new HashMap<String,Object>();
            //判断表单元素是否有网关字段
            if(StringUtils.isNotBlank(map.get("gf_gateway_value"))){
                vmap.put("gf_gateway_value",map.get("gf_gateway_value"));
            }
            taskService.complete(taskId,vmap);
            String formKey=task.getFormKey();

            //生成任务节点表单实例
            FormInstance fi=initFormInstanceAndFormValue(formKey,map,pi.getBusinessKey(),task.getId(),"task");
            TaskNodeExample taskNodeExample=new TaskNodeExample();
            taskNodeExample.createCriteria().andTaskIdEqualTo(taskId);
            TaskNode taskNode=null;
            List<TaskNode> taskNodeList=taskNodeService.selectByExample(taskNodeExample);
            if(taskNodeList!=null&&taskNodeList.size()>0){
                taskNode=taskNodeList.get(0);
                taskNode.setAppealId(pi.getBusinessKey());
                taskNode.setEndTime(new Date());
                if(fi!=null){
                    taskNode.setFormInstanceId(fi.getId());
                }
                taskNode.setProcessInstanceId(pi.getId());
                taskNode.setTaskId(task.getId());
                taskNodeService.updateByPrimaryKeySelective(taskNode);
            }
        }
    }


}
